/*

   MCL STRING - A component library of the Sigh misc. C library.

   Copyright (c) 2011, Sulaiman A. Mustafa

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   ________________________________________________________________________
   
   Note:
      
      We deticate this humble effort to every muslim out there. It wont 
      be long now...
      
   ________________________________________________________________________   
   
   Readme:
   
   0- This is a Type-B MCL Compenent. No context initialization is 
      required.
   
   1- Some functions have two versions, one with a z at the end and the 
      other without. z functions are special functions that have been 
      optimized for mcl strings. Use them whenever posible for better 
      performance.
   
   2- mcl strings can not exceed 2^(sizeof(int)*8) bytes (hereinafter 
      characters). For systems that implement the int type as a 4 byte 
      number, mcl strings can not exceed 4294967296 chars (including 
      nul).
      
   3- any function that has the letter c appended to it's name generates
      new mcl strings with each call. These strings MUST be freed manually
      to avoid memory leaks. mcl_str also generates new mcl_strings but 
      does not have the c suffex.
      
   ________________________________________________________________________
   
   TODO Documentation Notes:
   
      1- mcl_str_t is just a normal char dypedef. It is used to remind the 
      programmer that the returned value is not a normal char string; for 
      the most part, it can be treated as such, but these strings must be
      freed after use
      
      2- Length of normal strings and mcl strings are computed difrently!
      one is determined by the location of null, the other by a value 
      stored when the string was last modified. This diffrence is what 
      gives mcl strings the edge over normal strings.
      
   _______________________________________________________________________
   
   TODO Code
   
      Turn off error status cheking -DMCL_STR_NOERRCHECK; by default, it's on
      and no function will tuch the string unless it has a OK status 
      
      Check that every function which creates strings resets the status byte to
      MCL_STR_OK, mcl_str and mcl_str_cz are already checked.
      


*/



#ifndef MCL_STR_H

#define MCL_STR_OK 0
#define MCL_STR_ENOMEM 1 //TODO ENOMEM -> EMEMORY
#define MCL_STR_EINDEX 2

#define MCL_STR_MCOUNT 0
#define MCL_STR_MLENGTH 1
#define MCL_STR_MSTART 2

typedef  char     mcl_str_t;
typedef  struct   {int start;int length;} mcl_str_selection_t;
typedef  struct   {int count; int error; mcl_str_selection_t *selections;} mcl_str_match_t;

#ifndef MCL_STR_ERR_C
extern mcl_str_t *mcl_str_nomemstr; 
#endif

mcl_str_t*     mcl_str(          
               const char* string
               );
               /* Returns an initialized mcl string containing 'string' */

mcl_str_t*     mcl_str_cz(
               mcl_str_t *string
               );
               /* Duplicates 'string' and returns the new duplicite */

int            mcl_str_len(
               mcl_str_t *string
               );
               /* Returns the length of a mcl string */

int            mcl_str_status(mcl_str_t* str);
               /* Returns the status of the last operation in which 'str' was
                  used. 0 for success, other for failiur */


               

mcl_str_t*     mcl_str_cat(                 
               mcl_str_t *dest,
               const char *src
               );
               /* Appends a string to a mcl string.
                  'src' is appended to 'dest', and the 'dest' is 
                  returned*/

mcl_str_t*     mcl_str_cat_z(               
               mcl_str_t *dest,             
               mcl_str_t *src               
               );
               /* Catenate two mcl strings. 
                  'src' is appended to 'dest', and 'dest' is returned. This is 
                  more effeciant that the normal version. The resulting string 
                  is returned */

mcl_str_t*     mcl_str_sub_c(
               const char *src,
               int from, 
               int length
               );
               /* see mcl_str_sub_cz */
               
mcl_str_t*     mcl_str_sub_cz(
               mcl_str_t *src, 
               int from, 
               int length
               );
               /* returns a substring from 'src'. 'from' can be 0, FIXME
                  negative. ot positive.
                     -  positive numbers are treated as normal indexes
                     -  negative numbers are added to the total length of the
                        string, effectivly calculating the index distance from
                        the end of the string to the begining.
                     -  limits:
                        -  if both limits are positive, 'from' is inclusive 
                           while the other is exclusive.
                        -  if both limits are negative, 'from' is exclusive 
                           while the other is inclusive.                  
                        -  if 'from' is positive and 'until' is negative, both
                           are inclusive.
                        -  if 'from' is negative and 'until' is positive, both 
                           are exclusive.
                     length can be 0, positive, or -1 (which means "till the 
                     ebd of the string")
                  */

mcl_str_t*     mcl_str_insert(
               mcl_str_t* str, 
               const char* section,
               int start, 
               int length
               );
               /* insert 'section' at 'start' of 'str' and overwrite 'length' */

mcl_str_t*     mcl_str_insert_z(
               mcl_str_t* str, 
               mcl_str_t* section, 
               int start, 
               int length
               );
               /* insert 'section' at 'start' of 'str' and overwrite 'length' */
               
mcl_str_t*     mcl_str_free(
               mcl_str_t *string
               );
               /* Frees the resorses allocated to the mcl string 'string' */
               


mcl_str_match_t*     mcl_str_match_c(
                     const char *str, 
                     const char *pat
                     );
               /* Reterns an object contining a list of all occerances of 
                  'pat' in str. The returned mcl_str_match_t* must be freed 
                  using mcl_str_free_match_results(). The function continues 
                  to search through 'str' until a nul is found*/
                  
mcl_str_match_t*     mcl_str_match_free(
                     mcl_str_match_t* p
                     );
               /* frees the memory allocated to hold the results of  
                  mcl_str_match_c(). */
                  
int            mcl_str_mprobe(
               mcl_str_match_t* m, 
               int o, 
               int i
               );
               /* an interface to the mcl_str_match_t object. 'o' can be MCOUNT, 
                  MLENGTH, or MSTART whcich returns the number of matches, the
                  length of the 'i'th match, or the starting position of the 
                  'i'th match, respectivly. 
                  -1 is returned upon error.
               */
                  
mcl_str_t* mcl_str_replace(
               mcl_str_t* str, 
               const char *which,
               const char *what
               );
mcl_str_t*     mcl_str_replace_z(
               mcl_str_t* str,
               const char *which,
               mcl_str_t* what
               );
               /* Finds 'which' and replaces it with 'what' */
               
mcl_str_t*     mcl_str_match_delete(mcl_str_t* str, 
               const char *which
               );
               /* Finds 'which' and removes it */




/*FIXME: check if indentiation is allowed for prerosessor directives*/


/* *****************************************************************************
                                    
                                    <!>
                                    
   WARNING: PRIVATE DEFINITIONS INTENDED FOR INTERNAL USE. THE FOLLOWING 
            PROTOTYPES AND OTHER DEFINITIONS ARE NOT GARENTEED TO STAY THE
            SAME FROM VERSION TO VERSION.
            
            DO NOT USE. 

   *****************************************************************************
*/

/* Extra bytes to accomadate the length variable, the nul char, and the error byte */
#define MCL_STR_EXTRA_BYTES (sizeof(int)+2)

#define mcl_str__lenaddr(X) ((int *) (X)) /* returns an int pointer to the first four bytes of the string */
#define mcl_str__straddr(X) ((X)+sizeof(int)+1) /* moves to string sec */
#define mcl_str__erraddr(X) ((char *) ((X)+sizeof(int))) /* moves to error sec */

#define mcl_str__prepare(X)  ((X)=(X)-sizeof(int)-1) // rewinds the pointer

#define mcl_str__terminate(X) (mcl_str__straddr(X)[*mcl_str__lenaddr(X)]='\0')

#define mcl_str__checkmemalloc(X, Y) {\
if(!(X)){\
   fprintf(stderr, Y " [ENOMEM] (Could not allocate enough memory)\n");\
   return mcl_str___seterr(NULL, MCL_STR_ENOMEM);\
}}

mcl_str_t* mcl_str___stretch(mcl_str_t* buf, int size);
mcl_str_t* mcl_str___sct(mcl_str_t* DEST, const char *SRC, int LEN);
mcl_str_t* mcl_str___seterr(mcl_str_t* str, int err);/*
sets the status byte to 'err'.

NULL as the first byte returns a freeable empty string with the status byte
set, or a non-freeable string in the case of ENOMEM.

Warning, This function will be a major source of memory leaks if it is not used
correctly. This fucntion will always generate new strings unless str is not null
AND the error is not ENOMEM.
*/


/*debug*/
#ifdef MCL_STR_DEBUG
#define mcl_str__debug(X) X
#else
#define mcl_str__debug(X) ;
#endif

#define MCL_STR_H
#endif


